﻿/**
* JsonServiceClient.js
* 
* @author Demis Bellot
* @version 1.0
* @requires jQuery.js, jquery-json.js
* 
* Copyright 2009, Demis Bellot
* http://code.google.com/p/ajaxstack/
*/

function JsonServiceClient(baseUri, type)
{
	JsonServiceClient.$baseConstructor.call(this);

	//should similar to "http://localhost/Service.Host.WebService/Public/Json/SyncReply/"
	this.baseSyncReplyUri = Path.combine(baseUri, "Json/SyncReply");
	this.baseAsyncOneWayUri = Path.combine(baseUri, "Json/AsyncOneWay");

	if (!JSN.cache)
	{
		this.log.debug("CACHE: JSON: " + this.baseSyncReplyUri);
		JSN.setCacheOptions({ size: 0 });
	}
}
var JSN = JsonServiceClient;
JSN.extend(ASObject, { type: "AjaxStack.JsonServiceClient" }, {

	send: function(webMethod, request, onSuccess, onError, ajaxOptions)
	{
		var startCallTime = new Date();
		var requestUrl = Path.combine(this.baseSyncReplyUri, webMethod);
		var id = JsonServiceClient.id++;

		var canCache = !ajaxOptions || ajaxOptions.type != "POST";
		var cacheKey = S.toString(webMethod, request);

		if (canCache)
		{
			var response = JSN.cache.getValue(cacheKey);
			if (response)
			{
				JSN.log.debug("loaded from cache: " + webMethod);
				if (onSuccess) onSuccess(new ResultEvent(response));
				return;
			}
		}

		var options = {
			type: "GET",
			url: requestUrl,
			data: request,
			dataType: "json",
			success: function(response)
			{
				var endCallTime = new Date();
				var callDuration = endCallTime.getTime() - startCallTime.getTime();
				if (!response)
				{
					JSN.log.warn(webMethod + ": returned empty response. time taken: " + callDuration + " ms");
					if (onSuccess) onSuccess(null);
					return;
				}
				else
				{
					JSN.log.debug(webMethod + ": server response time : " + callDuration + " ms");
				}

				if (isResponseSuccessful(response.ResponseStatus, onError))
				{
					if (canCache) JSN.cache.add(cacheKey, response);

					if (onSuccess) onSuccess(new ResultEvent(response));
					JSN.onSuccess({ id: id, webMethod: webMethod, request: request,
						response: response, durationMs: callDuration
					});
				}
				else
				{
					JSN.onError({ id: id, webMethod: webMethod, request: request,
						error: OperationResult.Parse(response.ResponseStatus),
						durationMs: callDuration
					});
				}
			},
			error: function(xhr, desc, exceptionobj)
			{
				var endCallTime = new Date();
				var callDuration = endCallTime.getTime() - startCallTime.getTime();

				//alert("ajax request error: " + xhr.responseText);
				try
				{
					JSN.log.error(xhr.responseText);
					if (onError) onError(xhr.responseText);
				}
				catch (e) { }
				JSN.onError({ id: id, webMethod: webMethod, request: request,
					error: xhr.responseText, durationMs: callDuration
				});
			}
		};

		O.merge(options, ajaxOptions);
		//$.extend(options, ajaxOptions);

		var ajax = $.ajax(options);
	},

	//Sends a HTTP 'GET' request on the QueryString
	getFromService: function(webMethod, request, onSuccess, onError)
	{
		this.send(webMethod, request, onSuccess, onError);
	},

	//Sends a HTTP 'POST' request as key value pair formData
	postFormDataToService: function(webMethod, request, onSuccess, onError)
	{
		this.send(webMethod, request, onSuccess, onError, { type: "POST" });
	},

	//Sends a HTTP 'POST' request as JSON
	postToService: function(webMethod, request, onSuccess, onError)
	{
		var jsonRequest = $.compactJSON(request);
		//$.dump({ request:request, json: jsonRequest });
		this.send(webMethod, jsonRequest, onSuccess, onError,
			{ type: "POST", processData: false, contentType: "application/json; charset=utf-8" });
	}
});

JSN.UNDEFINED_NUMBER = 0;
JSN.id = 0;
JSN.onError = function(args) { };
JSN.onSuccess = function(args) { };
JSN.setCacheOptions = function(options)
{
	JSN.cache = new Cache(options);
}

function isResponseSuccessful(responseStatus, onError)
{
	//if there is no responseStatus then there is no way to work out if it was an error.
	if (!responseStatus) return true;
	
	var result = OperationResult.Parse(responseStatus);
	if (!result.isSuccess)
	{
		if (onError == null)
		{
			JSN.log.error("isResponseSuccessful: result.isSuccess == false: " + result.errorCode);
			return;
		}
		var errorEvent = new ResponseErrorEvent(result);
		onError(errorEvent);
		return false;
	}
	return true;
}

function OperationResult()
{
	this.isSuccess = false;
	this.message = "";
	this.errorCode = "";
	this.stackTrace = "";
	this.fieldErrors = [];
	this.fieldErrorMap = {};
}
OperationResult.prototype.toString = function()
{
	return this.errorCode + ": " + this.message;
}
OperationResult.prototype.getFieldErrors = function()
{
	return this.fieldErrors;
}
OperationResult.prototype.getFieldErrorMap = function()
{
	return this.fieldErrorMap;
}
OperationResult.Parse = function(responseStatus)
{
	var result = new OperationResult();
	result.isSuccess = is.Empty(responseStatus.ErrorCode);
	result.errorCode = responseStatus.ErrorCode;
	result.message = responseStatus.Message;
	result.errorMessage = responseStatus.ErrorMessage;
	result.stackTrace = responseStatus.StackTrace;

	A.each(responseStatus.FieldErrors, function(objError)
	{
		var error = new FieldError(objError.ErrorCode, objError.FieldName, objError.ErrorMessage);
		result.fieldErrors.push(error);

		if (!is.Empty(error.fieldName))
		{
			result.fieldErrorMap[error.fieldName] = error;
		}
	});

	return result;
}

function ResponseErrorEvent(result)
{
	this.result = result;
}

function FieldError(errorCode, fieldName, errorMessage)
{
	this.errorCode = errorCode;
	this.fieldName = fieldName;
	this.errorMessage = errorMessage || '';
}